package pers.hl.library.auth;

import com.google.gson.reflect.TypeToken;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import pers.hl.library.common.Const;
import pers.hl.library.common.Response;
import pers.hl.library.common.ServerConfig;
import pers.hl.library.po.User;
import pers.hl.library.utils.GsonUtils;
import pers.hl.library.utils.LogUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 认证拦截器
 */
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private ServerConfig serverConfig;

    private static final List<String> WHITE_LIST = new ArrayList<>();

    /**
     * 拦截地址白名单
     */
    static {
        WHITE_LIST.add(Const.Url.URL_LOGIN);
        WHITE_LIST.add(Const.Url.URL_ERROR);
        WHITE_LIST.add(Const.Url.URL_SIGN_UP);
        WHITE_LIST.add("test");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        String serverHost = serverConfig.getUrl();
        LogUtils.i(String.format("----------来自 %s 的请求-------  【请求路径】：[%s]", request.getRemoteAddr(), serverHost + uri));
        request.getRemoteHost();
        // 白名单放行
        if (inWhiteList(uri)) {
            return true;
        }
        // 没有部署的接口不拦截
        // 2020/11/7 todo  由于用了 @PathVariable 注解，导致列表获取到的url还是格式化的，无法与实际请求地址进行匹配，故先不做404异常处理
        // 其实根本不需要这里，因为现在可以直接全局捕获404异常了
//        String applicationName = SpringBeanTool.getApplicationContext().getApplicationName();
//        boolean urlExist = true;
//        // 得到的list中不包含项目名，但是uri包含，做下处理
//        List<String> list = MyUtils.getAllUrl();
//        if (!list.contains(uri.replace(applicationName, ""))) {
//            urlExist = false;
//        }
//        if (!urlExist) {
//            throw new PathException("您访问的地址不存在");
//        }
        return authVerify(request, response);
    }

    /**
     * 校验token
     */
    private boolean authVerify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String token = request.getHeader(Const.Auth.HEADER);
        if (StringUtils.isEmpty(token)) {
            throw new AuthException("token不能为空");
        }
        // 验证token
        boolean pass = JwtUtils.verifyPass(token);
        if (pass) {
            /* token验证通过 设置 userId 用户身份ID */
            Claims claims = JwtUtils.parseToken(token);
            LogUtils.i("body=" + claims.toString());
            LogUtils.i("签发时间" + claims.getIssuedAt());
            LogUtils.i("过期时间" + claims.getExpiration());
            Type type = new TypeToken<Map<Object, Object>>(){}.getType();
            Map<String, String> map = GsonUtils.fromJson(claims.toString(), type);
            Object userStr = map.get("user");
            String userJson = GsonUtils.toJson(userStr);
            User user = GsonUtils.fromJson(userJson, User.class);
            LogUtils.i("解析得到user" + user.toString());
            request.setAttribute(Const.Keys.KEY_USER, user);
            return true;
        }
        // token验证失败
        if (JwtUtils.isTokenExpired(token)) {
            throw new AuthException();
        }
        throw new AuthException();
    }

    private String genJsonFailResult(Response response) {
        return GsonUtils.toJson(response);
    }

    /**
     * 判断请求路径是否在白名单中
     */
    private boolean inWhiteList(String uri) {
        for (String s : WHITE_LIST) {
            if (uri.contains(s)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 输出json结果
     *
     * @param response
     * @param jsonResult
     */
    @Deprecated
    private void writeResult(HttpServletResponse response, String jsonResult) {
        try {
            OutputStream out = response.getOutputStream();
            response.setContentType("text/html;charset=UTF-8");
            out.write(jsonResult.getBytes(StandardCharsets.UTF_8));
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
